/**
 * Recife Action Tools - RAT
 * Centro de Inform�tica - CIn - UFPE
 *
 * Biblioteca para Gera��o de Analisador L�xico
 */

package br.ufpe.abaco.ParserGen.lexer;

import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import br.ufpe.abaco.ParserGen.parser.TerminalSymbol;

/**
 * Esta classe implementa uma fachada para o analisador l�xico.
 *
 * @author     Monique Monteiro
 * Criada em 09/10/02
 */
public class LexerFacade implements java.io.Serializable  {

	/*
	 * Express�o regular que representa a disjun��o (choice ou escolha) de todas as 
	 * express�es regulares aceitas pelo analisador l�xico.
	 */
	private RegExp regularExpression;

	/*
	 * Reconhecedor l�xico capaz de processar uma dada entrada.
	 */
	private LexicalProcessor processor;

	/**
	 * Insere uma regra no analisador l�xico para a qual N�O DEVE ser gerado um token 
	 * espec�fico.  
	 * 
	 * @param rule	express�o regular que representa a regra
	 */
	public void insertRule(RegExp rule) {
		if (regularExpression == null) {
			regularExpression = rule;
		} else {
			regularExpression = RegExp.choice(regularExpression, rule);
		}
	}

	/**
	 * Insere uma regra para strings que devem ser ignoradas durante a an�lise l�xica.
	 * 
	 * @param rule	express�o regular que representa a string a sere ignorada
	 */
	public void insertBlank(RegExp rule) {
		TerminalSymbol.BLANK.setRegularExpression(rule);
		insertRule(rule, TerminalSymbol.BLANK);
	}

	/**
	 * Insere uma regra no analisador l�xico para a qual DEVE ser gerado um token 
	 * espec�fico.
	 * 
	 * @param rule		express�o regular que representa a regra
	 * @param terminal	s�mbolo terminal que deve ser associado � regra passada como 
	 * 					par�metro
	 */
	public void insertRule(RegExp rule, TerminalSymbol terminal) {
		insertRule(RegExp.sequence(rule, RegExp.produce(terminal)));
	}

	/**
	 * Especifica que uma determinada regra N�O DEVE ser associada a um dado terminal.
	 * Especialmente �til para evitar que palavras reservadas sejam confundidas como
	 * identificadores da linguagem.
	 * 
	 * @param rule		express�o regular que representa a regra
	 * @param terminal	s�mbolo terminal que N�O DEVE ser associado � regra passada como
	 * 					par�metro 
	 */
	public void rejectRule(RegExp rule, TerminalSymbol terminal) {
		insertRule(RegExp.sequence(rule, RegExp.reject(terminal)));
	}

	/**
	 * Faz a an�lise l�xica da entrada, representada como uma string.  N�O suporta contagem
	 * de linhas.
	 * 
	 * @param input						string que representa a entrada
	 * @return Token[]					seq��ncia de tokens retornada, caso n�o haja erro
	 * @throws LexicalErrorException	se houver erro l�xico
	 */
	public Token[] analyse(String input) throws LexicalErrorException {
		Vector tokens = new Vector();

		if (!input.equals("")) {

			if (processor == null) {
				processor = new LexicalProcessor(regularExpression);
			}

			boolean status = processor.process(input, tokens);
			//adiciona o lexema que indica o final da entrada
			tokens.addElement(Token.EOF);
			//obt�m as informa��es de linhas e colunas
			//setTokensPositions(input, tokens);

			if (!status) {
				Token lastToken = (Token) tokens.elementAt(tokens.size() - 1);
				throw new LexicalErrorException(input.substring(lastToken.getStartPos()));
			}

		}

		Token[] result = new Token[tokens.size()];
		return (Token[]) tokens.toArray(result);
	}

	/**
	 * Divide a string de entrada em linhas e seta as informacoes de linhas e colunas
	 * nos tokens.
	 * 
	 * @param input
	 * @param tokens
	 */
//	private void setTokensPositions(String input, Vector tokens) {
//		int lastLine = 0, lastIndex = 0;
//		List lines = splitInLines(input);
//		int linesNumber = lines.size();
//		Iterator it = tokens.iterator();
//
//		while (it.hasNext()) {
//			Token token = (Token) it.next();
//
//			for (int i = lastLine; i < linesNumber; i++) {
//				String line = (String) lines.get(i);
//				int index = line.indexOf(token.text, lastIndex);
//
//				if (index != -1) {
//					lastIndex = index + token.text.length();
//					token.line = i + 1;
//					token.startPos = index + 1;
//					lastLine = i;
//					break;
//				} else {
//					//muda de linha
//					lastIndex = 0;
//				}
//			}
//		}
//
//	}

	/**
	 * Divide uma string em linhas.  Em fachadas que recebem streams como entrada, n�o
	 * � necess�rio um m�todo igual a este, pois h� formas mais eficientes de obter 
	 * diretamente as linhas.
	 *  
	 * @param input		a string a ser dividida em linhas
	 * @return	List	uma seq��ncia de linhas
	 */
	private List splitInLines(String input) {
		List result = new Vector();
		int length = input.length();
		int index = 0, oldIndex = 0;

		while (index < length) {
			index = input.indexOf('\n', oldIndex);

			if (index != -1) {
				result.add(input.substring(oldIndex, index));
				oldIndex = index + 1;
			} else if (oldIndex < length) {
				//ultima linha
				result.add(input.substring(oldIndex, length));
				break;
			} else {
				break;
			}
		}

		return result;
	}

}






